// definition of the function prototypes in mem_space.h

// check pointer
owl_bool owl_check_mem_space_pointer(owl_byte * src, owl_umax size)
{
  return (src != NULL && size >= sizeof(owl_mem_space));
}

// check members
owl_bool owl_check_mem_space_members(owl_byte * ptr, owl_umax size)
{
  return (ptr != NULL && size != 0);
}

// check all
owl_bool owl_check_mem_space_all(owl_mem_space * src, owl_umax size)
{
  return owl_check_mem_space_pointer((owl_byte*) src, size)
         && owl_check_mem_space_members(src -> ptr, src -> size);
   
}

// allocate memory and create the structure
owl_mem_space * owl_create_mem_space(owl_umax size)
{  
  // check global error
  owl_mem_space * space = NULL;
  if (owl_check_mem_space_members(NOT_NULL, size) == owl_false)
    goto end;
  // create a new owl_mem_space variable into
  // a free slot in the memory space table
  for (owl_umax i = 0; i < OWL_MEM_SPACE_TB_SIZE; i++)
    if (OWL_MEM_SPACE_TB[i].ptr == NULL && OWL_MEM_SPACE_TB[i].size == 0) {
      // allocate memory with calloc()
      OWL_MEM_SPACE_TB[i].ptr = calloc(size, 1);
      if (OWL_MEM_SPACE_TB[i].ptr == NULL)
        goto end;
      OWL_MEM_SPACE_TB[i].size = size;
      OWL_MEM_SPACE_TB_FREE_SLOTS--;
      space = &OWL_MEM_SPACE_TB[i];
      break;
    }
  // no space left on the table
  if (space == NULL) 
    OWL_MEM_SPACE_GLOB_ERR = OWL_MEM_SPACE_TB_FULL;
  // return whatever happened
  end:
  return space;
}

// free the memory used by the structure
owl_umax owl_free_mem_space(owl_mem_space * space)
{  
  // check if the pointer is valid
  owl_umax tmp = OWL_MEM_SPACE_TB_FREE_SLOTS;
  if (owl_check_mem_space_all(space, sizeof(owl_mem_space))) {
    // search for the equivalent structure in the space table to remove it
    // this function will enforce freeing a structure that was created
    // by the create function, it wont free anything else
    for (owl_umax i = 0; i < OWL_MEM_SPACE_TB_SIZE; i++)
      if (space -> ptr == OWL_MEM_SPACE_TB[i].ptr) {
        free(space -> ptr);
        OWL_MEM_SPACE_TB[i].ptr = NULL;
        OWL_MEM_SPACE_TB[i].size = 0;
        OWL_MEM_SPACE_TB_FREE_SLOTS++;
        break;
      }
  }  
  // return free slot count
  if (OWL_MEM_SPACE_TB_FREE_SLOTS <= tmp && space != NULL)
    return 0;
  return OWL_MEM_SPACE_TB_FREE_SLOTS;
}

// print the information of the struct
owl_umax owl_print_mem_space(owl_byte * src, owl_umax size)
{
  // check params
  printf("OWL_MEM_SPACE info:\n");
  printf("  Struct info: %p, %llu\n", src, size);
  if (!owl_check_mem_space_pointer(src, size))
    return 0;
  
  // otherwise print the structure info
  owl_mem_space * space = (void *) src;
  printf("  Array pointer: %p\n", space -> ptr);
  printf("  Array size (bytes): %llu\n", space -> size);
  return sizeof(owl_mem_space);
}

// to compare 2 mem_spaces
owl_char owl_comp_mem_space(owl_byte * mem_space1, owl_byte * mem_space2)
{
  owl_mem_space * msp1 = (void *) mem_space1,
                * msp2 = (void *) mem_space2;
  // check params
  if (!owl_check_mem_space_all(msp1, sizeof(owl_mem_space))
      || !owl_check_mem_space_all(msp2, sizeof(owl_mem_space)))
    return owl_comp_err;
  // else, do the comparison (sizes will be the way)
  if (msp1 -> size < msp2 -> size)
    return owl_comp_less;
  else if (msp1 -> size > msp2 -> size)
    return owl_comp_greater;
  return owl_comp_equal;
}

// function to print the error code stored in OWL_MEM_SPACE_GLOB_ERR
void owl_print_mem_space_tb_err(void)
{
  // evaluate the error
  switch(OWL_MEM_SPACE_GLOB_ERR) {
    case OWL_MEM_SPACE_NO_ERR: 
      printf("OWL_MEM_SPACE_ERR: NO ERROR (%d)\n", OWL_MEM_SPACE_NO_ERR);
      break;
    case OWL_MEM_SPACE_TB_FULL: 
      printf("OWL_MEM_SPACE_ERR: SPACE TABLE FULL (%d)\n", OWL_MEM_SPACE_TB_FULL);
      break;
    case OWL_MEM_SPACE_CALLOC_FAIL: 
      printf("OWL_MEM_SPACE_ERR: CALLOC FAIL (%d)\n", OWL_MEM_SPACE_CALLOC_FAIL);
      break;
  }
  return;
}
